package secondary.employmentlaw;

import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.StringTokenizer;

import jade.content.Concept;
import jade.content.ContentElement;
import jade.content.lang.Codec;
import jade.content.lang.sl.SLCodec;
import jade.content.onto.Ontology;
import jade.content.onto.basic.Action;
import jade.core.behaviours.CyclicBehaviour;
import jade.lang.acl.ACLMessage;
import jade.util.leap.ArrayList;
import jade.util.leap.List;
import ontology.EmploymentLawOntology;
import ontology.UMCourtLOntology;
import ontology.actions.general.AddNewResult;
import ontology.actions.general.GetSimilarities;
import ontology.actions.general.ReturnSimilarities;
import ontology.concepts.employmentlaw.Case;
import ontology.concepts.employmentlaw.Norm;
import secondary.employmentlaw.EvaluatorL.receiveBehaviour;
import classes.UMCourtAgent;

public class NearestNeighbourL extends UMCourtAgent 
{
	private Codec codec = new SLCodec();
	private Ontology ontology = EmploymentLawOntology.getInstance();
	long stime = 0;
	private static Connection C;
	private static Statement Stmt;
	
	protected void setup() 
	{
		System.out.println(getDate()+ ": "+this.getLocalName()+" agent is now running...");
		registerAgent((String)getArguments()[0]);
		
		// Register language and ontology
	    getContentManager().registerLanguage(codec);
	    getContentManager().registerOntology(ontology);
		
		this.addBehaviour(new receiveBehaviour());
	}
	
	class receiveBehaviour extends CyclicBehaviour
	{
		@Override
		public void action() 
		{
			ACLMessage msg = receive();
	        if (msg == null) 
	        { 
	        	block(); 
	        	return; 
	        }
	        
	        try 
			{
	        	ContentElement content = getContentManager().extractContent(msg);
		        Concept action = ((Action)content).getAction();
         		
		        switch (msg.getPerformative()) 
		        {
		        case (ACLMessage.REQUEST):
			        if (action instanceof GetSimilarities)
			        {
			        	stime = System.currentTimeMillis();
			        	content = getContentManager().extractContent(msg);
			            GetSimilarities gs = (GetSimilarities) ((Action)content).getAction();
			            
			        	myprintln("Received request for similarities: "+msg.getConversationId());
			        	Case newCase = (Case) gs.getNewC();
			        	ArrayList cases = gs.getCases();
			        	ArrayList caseids = new ArrayList();
			        	
			        	for (int i=0;i<cases.size();i++)
			        		caseids.add(((Case)cases.get(i)).getID());
			        	
			        	String method = gs.getMethod();
			        	ArrayList similarities = new ArrayList();
			        	
			        	if (method.equals("nn"))
			        	{
			        	
			        		myprintln("Using Nearest Neighbour algorithm");
				        	//for each case in cases, compute similarity with newCase
				        	Float sim = new Float(0);
				        	for (int i=0;i<cases.size();i++)
				        		similarities.add(computeSimilarity(newCase, (Case)cases.get(i)));
			        	}
			        	else if (method.equals("cs"))
				        	{
			        			myprintln("Using Cosine Similarity algorithm");
				        		File file = new File("data.arff");
				        	    FileInputStream fis = null;
				        	    BufferedInputStream bis = null;
				        	    DataInputStream dis = null;
				        	    
				        	    /*
				        	     * Build vectorial representation of new case
				        	     * */
				        	    C = connectDB();
				        	    Statement select = C.createStatement();
				    			ResultSet result = select.executeQuery("SELECT DISTINCT article, number, item FROM umCourtl.norms");
				    			ArrayList norms = new ArrayList(); //all the possible norms

				    			//build arraylist with all the norms
				    			while (result.next())
				    			{
				    				int article = result.getInt(1);
				    				int number = result.getInt(2);
				    				String item = result.getString(3);
				    				
				    				String norm = new String();
				    				
				    				if (number == 0)
				    					norm = article+"";
				    				else
				    					if (item == null)
				    						norm = article+"-"+number+"";
				    					else
				    						norm = article+"-"+number+"-"+item;
				    				
				    				if (!norms.contains(norm))
				    					norms.add(norm);
				    			}
				    			
				    			//see which norms are addressed by the new case
				    			long[] ncase = new long[norms.size()+2]; // +1 caseid, +1 partywins
				    			ncase[0] = Long.parseLong(newCase.getID());
					    		List l1 = newCase.getEmployeeNorms();
					    		List l2 = newCase.getEmployerNorms();
					    		
					    		for (int i=0;i<norms.size();i++)
					    		{
					    			String str = (String) norms.get(i);
					    			StringTokenizer st = new StringTokenizer (str, "-");
					    			Norm norm = new Norm();
					    			switch(st.countTokens())
					    			{
					    				case 1: norm.setArticle(str);
					    						break;
					    				case 2: norm.setArticle(st.nextToken());
					    						norm.setNumber(st.nextToken());
					    						break;
					    				case 3: norm.setArticle(st.nextToken());
			    								norm.setNumber(st.nextToken());
			    								norm.setItem(st.nextToken());
			    								break;
					    			}

					    			boolean found = false;
					    			for (int j=0;j<l1.size() && !found;j++)
					    			{
					    				if (norm.equals(l1.get(j)))
					    					found = true;
					    			}
					    			for (int j=0;j<l2.size() && !found;j++)
					    			{
					    				if (norm.equals(l2.get(j)))
					    					found = true;
					    			}
					    			if (found)
					    				ncase[i+1] = 1;
					    			else
					    				ncase[i+1] = 0;
					    		}
					    		if (newCase.getParty1wins())
					    			ncase[norms.size()+1] = 1;
					    		else
					    			ncase[norms.size()+1] = 0;

					    		
					    		/*
					    		 * Parse the file with the case matrix and select the vectors that correspond to the pre-selected cases 
					    		 * 
					    		 * */
					    		try 
				        	    {
				        	    	fis = new FileInputStream(file);
				        	    	bis = new BufferedInputStream(fis);
				        	    	dis = new DataInputStream(bis);
				        	    	
				        	    	ArrayList temp = new ArrayList();
				        	    	
				        	    	//compute similarity for each line
				        	    	boolean found = false;
				        	    	int count = 0;
				        	    	while (dis.available() != 0) 
				        	    	{
				        		        String line = dis.readLine();
				        		        double similarity = new Float(0);
				        		        
				        		        if (found)
				        		        {
				        		        	
				        			        StringTokenizer st = new StringTokenizer (line, ",");
				        		        	int n = st.countTokens();
				        		    		long[] c = new long[n];
				        		    		String caseID = st.nextToken();
				        		    		c[0] = Long.parseLong(caseID); //caseid
				        		    		
				        		    		if (caseids.contains(caseID))
				        		    		{
				        		    			temp.add(caseID);
				        		    			count++;
					        		        	double up = 0;
					        		    		double abs1 = 0;
					        		    		double abs2 = 0;
					        		    		
					        		    		for (int i=1;i<n;i++)
					        		        		c[i] = Integer.parseInt(st.nextToken());
		
					        		    		//compute up part of the cosine formula
					        		    		for (int i=1;i<n;i++) //caseID is not compared
					        		        		up += (ncase[i] * c[i]);
					        		    		
					        		    		//compute abs of each vector
					        		    		for (int i=1;i<n;i++) //caseID is not compared
					        		        		abs1 += ncase[i] * ncase[i];
					        		    		abs1 = Math.sqrt(abs1);
					        		    		
					        		    		for (int i=1;i<n;i++) //caseID is not compared
					        		        		abs2 += c[i] * c[i];
					        		    		abs2 = Math.sqrt(abs2);
					        		        	
					        		    		similarity = up / (abs1 * abs2);
					        		        	similarities.add(similarity);
				        		    		}
				        		        }
				        		        else
				        		        	if(line.contains("@data"))
				        			        	found = true;
				        	    	}
				        	    	
				        		    // dispose all the resources after using them.
				        		    fis.close();
				        		    bis.close();
				        		    dis.close();
	
				        	    } 
				        	    catch (FileNotFoundException e) {e.printStackTrace();} 
				        	    catch (IOException e) {e.printStackTrace();}
				        	}
			        		
			        	
			        	ReturnSimilarities rs = new ReturnSimilarities();
			        	rs.setCaseList(gs.getCases());
			        	rs.setComparedCase(gs.getNewC());
			        	rs.setSimilarities(similarities);
			        	
			        	ACLMessage reply = new ACLMessage(ACLMessage.INFORM);
			        	reply.setLanguage(codec.getName());
			        	reply.setOntology(ontology.getName());
			        	reply.setConversationId(msg.getConversationId());
						
						try 
						{
							
							sendNewTime(method, stime);
							getContentManager().fillContent(reply, new Action(msg.getSender(), rs));
							reply.addReceiver(msg.getSender());
							send(reply);
							myprintln("Sending list of computed similarities to: "+msg.getSender());
						}
						catch (Exception ex) { ex.printStackTrace(); }
			        }
		        }
			}
	        catch(Exception e){e.printStackTrace();}
		}
	}
	
	private float computeSimilarity(Case nc, Case c)
	{
		if (c.getEmployeeNorms() == null || c.getEmployerNorms() == null)
		{
			myprintln("WARNING: case "+c.getID()+" lacks norms! *****");
			return 0;
		}
		
		ArrayList norms1 = (ArrayList) nc.getEmployeeNorms();
		ArrayList norms2 = (ArrayList) c.getEmployeeNorms();
		float employeeSim = 0;
		float employerSim = 0;
		
		//determine amount of employee norms in common
		int intersect = 0;
		if (norms1.size() > norms2.size())
		{
			for (int i=0;i<norms1.size();i++)
			{
				for (int j=0;j<norms2.size();j++)
					if (((Norm)norms1.get(i)).equals((Norm)norms2.get(j)))
							intersect++;
			}
			employeeSim = intersect*100/norms1.size();
		}
		else
		{
			for (int i=0;i<norms2.size();i++)
			{
				for (int j=0;j<norms1.size();j++)
					if (((Norm)norms2.get(i)).equals((Norm)norms1.get(j)))
							intersect++;
			}
			employeeSim = intersect*100/norms2.size();
		}
		
		norms1 = (ArrayList) nc.getEmployerNorms();
		norms2 = (ArrayList) c.getEmployerNorms();
		//determine amount of employer norms in common
		intersect = 0;
		if (norms1.size() > norms2.size())
		{
			for (int i=0;i<norms1.size();i++)
			{
				for (int j=0;j<norms2.size();j++)
					if (((Norm)norms1.get(i)).equals((Norm)norms2.get(j)))
							intersect++;
			}
			employerSim = intersect*100/norms1.size();
		}
		else
		{
			for (int i=0;i<norms2.size();i++)
			{
				for (int j=0;j<norms1.size();j++)
					if (((Norm)norms2.get(i)).equals((Norm)norms1.get(j)))
							intersect++;
			}
			employerSim = intersect*100/norms2.size();
		}
		
		//Return mean of the two values
		return (employeeSim + employerSim) /2;
	}
	
}
